#pragma once

template <class T>
class IocContainer
{
    using FuncType = std::function<T* ()>;

private:
    std::map<std::string, FuncType> m_ObjectMap;

public:
    IocContainer() {}
    ~IocContainer() {}

private:
    void RegisterObject(std::string strKey, FuncType lpCreator)
    {
        if (m_ObjectMap.find(strKey) != m_ObjectMap.end())
        {
            throw std::invalid_argument("The key is exist.");
            return;
        }

        m_ObjectMap.emplace(strKey, lpCreator);
    }

public:
    template<class Drived>
    void RegisterCreator(std::string strKey)
    {
        FuncType function = [] { return new Drived(); };
        RegisterObject(strKey, function);
    }

    T* FindObject(std::string strKey)
    {
        if (m_ObjectMap.find(strKey) == m_ObjectMap.end())
            return nullptr;

        FuncType function = m_ObjectMap[strKey];
        return function();
    }

    std::shared_ptr<T> GetObjectPtr(std::string strKey)
    {
        T* ptr = FindObject(strKey);
        return std::shared_ptr<T>(ptr);
    }
};

struct ICar
{
    virtual void Test() = 0;
};

struct CBus : public ICar
{
    CBus() {}
    void Test() { std::cout << "CBus::Test()" << std::endl; }
};

struct CTrack : public ICar
{
    CTrack() {};
    void Test() { std::cout << "CTrack::Test()" << std::endl; }
};
